package ro.myApp.commons.tree;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.TreeSet;

import ro.myApp.commons.utils.CustomAssertion;

public class TreeStructureCompositTransformer {

	private static TreeStructureCompositTransformer instance;
	
	protected TreeStructureCompositTransformer() {}
	
	public static TreeStructureCompositTransformer getInstance (){
		if(null==instance)
			instance = new TreeStructureCompositTransformer();
		return instance;
	}
	
	public <E  extends TreeStructureComposit<E>> List<E> transformToList (E root) {
		List<E> nodeList = new LinkedList<E>();		
		if (root != null) {			
			this.addChildNodeToList(nodeList, root);
		}
		return nodeList;
	}

	public <E extends TreeStructureComposit<E>> E transformToTreeStructure (Collection<E> list){
		TreeSet<E> nodeList = new TreeSet<E>((Collection<E>) list);
		E root = null;
		E prevNode = null;
		E node;
		E parent=null;
		E firstNode = nodeList.first();
		boolean originalIsRoot = firstNode.isRoot();
		if (!firstNode.isRoot()){
			firstNode.setRoot(true);
		}
		for (Iterator<E> iterator = nodeList.iterator(); iterator.hasNext();) {
			node = iterator.next();			
			if (node.isRoot()) {
				root = node;				
			} else if (prevNode != null) {				
				if (node.getLevel()==prevNode.getLevel()) {
					parent = (E) prevNode.getParent();
					this.addOrderedChild((List<E>)parent.getChildren(), node);					
					node.setParent(parent);
				} else if(node.getLevel()>prevNode.getLevel()){ 
					this.addOrderedChild((List<E>)prevNode.getChildren(), node);					
					node.setParent(prevNode);
				}
				else{
					parent = findParent(node,prevNode);
					this.addOrderedChild((List<E>)parent.getChildren(), node);				
					node.setParent(parent);
				}				
			}
			prevNode = node;			
		}
		root.setRoot(originalIsRoot);
		return root;
	}

	private <E  extends TreeStructureComposit<E>> E findParent(E node, E prevNode) {
		E parent = (E) prevNode.getParent();
		int diffLevel = prevNode.getLevel();
		while(diffLevel!=node.getLevel()){
			diffLevel --;
			parent = (E) parent.getParent();
		}
		CustomAssertion.assertNotNull(parent, "failed to locate parent");
		return parent;
	}

	private <E  extends TreeStructureComposit<E>> void addOrderedChild(List<E> list, E child) {
		ListIterator<E> iterator = list.listIterator();
		E node;
		while (iterator.hasNext()) {
			node = iterator.next();
			if (child.getOrder()<=node.getOrder()){
				iterator.previous();
				break;
			}
		}
		iterator.add(child);
	}
	
	private <E  extends TreeStructureComposit<E>> void addChildNodeToList(List<E> nodeList, E root) {
		if (!root.isLeaf()) {
			for (Iterator<E> iterator = root.getChildren().iterator(); iterator.hasNext();) {
				addChildNodeToList(nodeList,iterator.next());
			}
		}
		nodeList.add(root);	
	}
}
